From 147cdd846502d261e2efa02a96ef39d238f8ec4f Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Tue, 24 Jan 2012 00:49:52 +0100 Subject: [PATCH] devicemanager,xi2: Implement smooth scrolling XInput >= 2.1 allows for implementing smooth scrolling, reporting the different scrolling axes as valuators. Any change in those will be reported as GdkEventScroll events with delta_x/y information. the older kind of scroll events is still handled, and emulated in devices able to provide smooth scrolling, setting _gdk_event_set_pointer_emulated() in that case. --- gdk/x11/gdkdevice-xi2.c | 101 +++++++++++++++++++++++++++++++++ gdk/x11/gdkdevicemanager-xi2.c | 99 +++++++++++++++++++++++++++++++- gdk/x11/gdkprivate-x11.h | 9 +++ 3 files changed, 206 insertions(+), 3 deletions(-) diff --git a/gdk/x11/gdkdevice-xi2.c b/gdk/x11/gdkdevice-xi2.c index 4b37a7a82f..d1139da01f 100644 --- a/gdk/x11/gdkdevice-xi2.c +++ b/gdk/x11/gdkdevice-xi2.c @@ -33,11 +33,22 @@ #endif +typedef struct _ScrollValuator ScrollValuator; + +struct _ScrollValuator +{ + guint n_valuator : 4; + guint direction : 4; + guint last_value_valid : 1; + gdouble last_value; +}; + struct _GdkX11DeviceXI2 { GdkDevice parent_instance; gint device_id; + GArray *scroll_valuators; }; struct _GdkX11DeviceXI2Class @@ -49,6 +60,7 @@ G_DEFINE_TYPE (GdkX11DeviceXI2, gdk_x11_device_xi2, GDK_TYPE_DEVICE) #ifdef XINPUT_2 +static void gdk_x11_device_xi2_finalize (GObject *object); static void gdk_x11_device_xi2_get_property (GObject *object, guint prop_id, GValue *value, @@ -110,6 +122,7 @@ gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkDeviceClass *device_class = GDK_DEVICE_CLASS (klass); + object_class->finalize = gdk_x11_device_xi2_finalize; object_class->get_property = gdk_x11_device_xi2_get_property; object_class->set_property = gdk_x11_device_xi2_set_property; @@ -134,6 +147,17 @@ gdk_x11_device_xi2_class_init (GdkX11DeviceXI2Class *klass) static void gdk_x11_device_xi2_init (GdkX11DeviceXI2 *device) { + device->scroll_valuators = g_array_new (FALSE, FALSE, sizeof (ScrollValuator)); +} + +static void +gdk_x11_device_xi2_finalize (GObject *object) +{ + GdkX11DeviceXI2 *device = GDK_X11_DEVICE_XI2 (object); + + g_array_free (device->scroll_valuators, TRUE); + + G_OBJECT_CLASS (gdk_x11_device_xi2_parent_class)->finalize (object); } static void @@ -777,6 +801,83 @@ _gdk_x11_device_xi2_translate_state (XIModifierState *mods_state, return state; } +void +_gdk_x11_device_xi2_add_scroll_valuator (GdkX11DeviceXI2 *device, + guint n_valuator, + GdkScrollDirection direction) +{ + ScrollValuator scroll; + + g_return_if_fail (GDK_IS_X11_DEVICE_XI2 (device)); + g_return_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device))); + + scroll.n_valuator = n_valuator; + scroll.direction = direction; + scroll.last_value_valid = FALSE; + + g_array_append_val (device->scroll_valuators, scroll); +} + +gboolean +_gdk_x11_device_xi2_get_scroll_delta (GdkX11DeviceXI2 *device, + guint n_valuator, + gdouble valuator_value, + GdkScrollDirection *direction_ret, + gdouble *delta_ret) +{ + guint i; + + g_return_val_if_fail (GDK_IS_X11_DEVICE_XI2 (device), FALSE); + g_return_val_if_fail (n_valuator < gdk_device_get_n_axes (GDK_DEVICE (device)), FALSE); + + for (i = 0; i < device->scroll_valuators->len; i++) + { + ScrollValuator *scroll; + + scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i); + + if (scroll->n_valuator == n_valuator) + { + if (direction_ret) + *direction_ret = scroll->direction; + + if (delta_ret) + *delta_ret = 0; + + if (scroll->last_value_valid) + { + if (delta_ret) + *delta_ret = valuator_value - scroll->last_value; + + scroll->last_value = valuator_value; + } + else + { + scroll->last_value = valuator_value; + scroll->last_value_valid = TRUE; + } + + return TRUE; + } + } + + return FALSE; +} + +void +_gdk_device_xi2_reset_scroll_valuators (GdkX11DeviceXI2 *device) +{ + guint i; + + for (i = 0; i < device->scroll_valuators->len; i++) + { + ScrollValuator *scroll; + + scroll = &g_array_index (device->scroll_valuators, ScrollValuator, i); + scroll->last_value_valid = FALSE; + } +} + gint _gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device) { diff --git a/gdk/x11/gdkdevicemanager-xi2.c b/gdk/x11/gdkdevicemanager-xi2.c index cf6a1742d8..2fb66e120b 100644 --- a/gdk/x11/gdkdevicemanager-xi2.c +++ b/gdk/x11/gdkdevicemanager-xi2.c @@ -241,6 +241,22 @@ translate_device_classes (GdkDisplay *display, valuator_info->resolution); } break; +#ifdef XINPUT_2_2 + case XIScrollClass: + { + XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info; + GdkScrollDirection direction; + + if (scroll_info->scroll_type == XIScrollTypeVertical) + direction = GDK_SCROLL_DOWN; + else + direction = GDK_SCROLL_RIGHT; + + _gdk_x11_device_xi2_add_scroll_valuator (GDK_X11_DEVICE_XI2 (device), + scroll_info->number, + direction); + } +#endif /* XINPUT_2_2 */ default: /* Ignore */ break; @@ -1036,6 +1052,47 @@ gdk_x11_device_manager_xi2_translate_core_event (GdkEventTranslator *translator, return TRUE; } +static gboolean +scroll_valuators_changed (GdkX11DeviceXI2 *device, + XIValuatorState *valuators, + gdouble *dx, + gdouble *dy) +{ + gdouble has_scroll_valuators = FALSE; + GdkScrollDirection direction; + guint n_axes, i, n_val; + gdouble *vals; + + n_axes = gdk_device_get_n_axes (GDK_DEVICE (device)); + vals = valuators->values; + *dx = *dy = 0; + n_val = 0; + + for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++) + { + gdouble delta; + + if (!XIMaskIsSet (valuators->mask, i)) + continue; + + if (_gdk_x11_device_xi2_get_scroll_delta (device, i, vals[n_val], + &direction, &delta)) + { + has_scroll_valuators = TRUE; + + if (direction == GDK_SCROLL_UP || + direction == GDK_SCROLL_DOWN) + *dy = delta; + else + *dx = delta; + } + + n_val++; + } + + return has_scroll_valuators; +} + static gboolean gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, GdkDisplay *display, @@ -1142,8 +1199,11 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, XIDeviceEvent *xev = (XIDeviceEvent *) ev; GdkDevice *source_device; - if (ev->evtype == XI_ButtonPress && + if (ev->evtype == XI_ButtonRelease && (xev->detail >= 4 && xev->detail <= 7)) + return FALSE; + else if (ev->evtype == XI_ButtonPress && + (xev->detail >= 4 && xev->detail <= 7)) { /* Button presses of button 4-7 are scroll events */ event->scroll.type = GDK_SCROLL; @@ -1163,6 +1223,8 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, event->scroll.y = (gdouble) xev->event_y; event->scroll.x_root = (gdouble) xev->root_x; event->scroll.y_root = (gdouble) xev->root_y; + event->scroll.delta_x = 0; + event->scroll.delta_y = 0; event->scroll.device = g_hash_table_lookup (device_manager->id_table, GUINT_TO_POINTER (xev->deviceid)); @@ -1172,6 +1234,9 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, gdk_event_set_source_device (event, source_device); event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); + + if (xev->flags & XIPointerEmulated) + _gdk_event_set_pointer_emulated (event, TRUE); } else { @@ -1233,6 +1298,36 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, { XIDeviceEvent *xev = (XIDeviceEvent *) ev; GdkDevice *source_device; + gdouble delta_x, delta_y; + + source_device = g_hash_table_lookup (device_manager->id_table, + GUINT_TO_POINTER (xev->sourceid)); + + if (scroll_valuators_changed (GDK_X11_DEVICE_XI2 (source_device), + &xev->valuators, &delta_x, &delta_y)) + { + event->scroll.type = GDK_SCROLL; + event->scroll.direction = GDK_SCROLL_SMOOTH; + + event->scroll.window = window; + event->scroll.time = xev->time; + event->scroll.x = (gdouble) xev->event_x; + event->scroll.y = (gdouble) xev->event_y; + event->scroll.x_root = (gdouble) xev->root_x; + event->scroll.y_root = (gdouble) xev->root_y; + event->scroll.delta_x = delta_x; + event->scroll.delta_y = delta_y; + + event->scroll.device = g_hash_table_lookup (device_manager->id_table, + GUINT_TO_POINTER (xev->deviceid)); + + gdk_event_set_source_device (event, source_device); + + event->scroll.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); + break; + } + else + _gdk_device_xi2_reset_scroll_valuators (GDK_X11_DEVICE_XI2 (source_device)); event->motion.type = GDK_MOTION_NOTIFY; event->motion.window = window; @@ -1245,8 +1340,6 @@ gdk_x11_device_manager_xi2_translate_event (GdkEventTranslator *translator, event->motion.device = g_hash_table_lookup (device_manager->id_table, GINT_TO_POINTER (xev->deviceid)); - source_device = g_hash_table_lookup (device_manager->id_table, - GUINT_TO_POINTER (xev->sourceid)); gdk_event_set_source_device (event, source_device); event->motion.state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index 98d7732fee..5eea9a48c3 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -255,6 +255,15 @@ gint _gdk_x11_device_xi2_get_id (GdkX11DeviceXI2 *device); GdkDevice * _gdk_x11_device_manager_xi2_lookup (GdkX11DeviceManagerXI2 *device_manager_xi2, gint device_id); +void _gdk_x11_device_xi2_add_scroll_valuator (GdkX11DeviceXI2 *device, + guint n_valuator, + GdkScrollDirection direction); +gboolean _gdk_x11_device_xi2_get_scroll_delta (GdkX11DeviceXI2 *device, + guint n_valuator, + gdouble valuator_value, + GdkScrollDirection *direction_ret, + gdouble *delta_ret); +void _gdk_device_xi2_reset_scroll_valuators (GdkX11DeviceXI2 *device); #endif -- 2.30.2